/* Copyright 2012 Dustin Mitchell <dmmitche [at] gmail [dot] com>
 *
 * Licensed under the GPLv3.  See included LICENSE.txt for full details.
 */

#include <sys/select.h>
#include <stdlib.h>

#include "sensor_manager.h"
#include "iterator.h"
#include "log.h"

int sensor_manager_create (struct sensor_manager *mgr)
{
  if (!mgr) {
    log (LOG_ERR, "invalid param");
    return -1;
  }

  if (hash_create (&mgr->sensors, NULL, NULL) == -1) {
    log (LOG_ERR, "unable to create hash");
    return -1;
  }

  return 0;
}

void sensor_manager_destroy (struct sensor_manager *mgr)
{
  if (!mgr) {
    log (LOG_ERR, "invalid param");
    return;
  }

  hash_destroy (&mgr->sensors);
}

int sensor_manager_register (struct sensor_manager *mgr, union sensor *sensor,
                             int sensor_id)
{
  if (!mgr || !sensor || sensor_id < 0) {
    log (LOG_ERR, "invalid param");
    return -1;
  }

  if (hash_exists (&mgr->sensors, &sensor_id, sizeof (sensor_id))) {
    log (LOG_ERR, "sensor id already registered");
    return -1;
  }

  if (hash_insert (&mgr->sensors, &sensor_id, sizeof (sensor_id), sensor)
      == -1) {
    log (LOG_ERR, "unable to insert element into hash");
    return -1;
  }

  log (LOG_DEBUG, "registered %d type %d", sensor_id, sensor->sensor.type);

  return 0;
}

int sensor_manager_read (struct sensor_manager *mgr,
                         union sensor_reading *reading)
{
  struct hash_iter beg, end;
  int sel;
  int nfds = 0;
  fd_set rd_set;

  if (!mgr || !reading) {
    log (LOG_ERR, "invalid param");
    return -1;
  }

  if (hash_begin (&mgr->sensors, &beg) == -1
      || hash_end (&mgr->sensors, &end) == -1) {
    log (LOG_ERR, "unable to create has iterators");
    return -1;
  }

  FD_ZERO (&rd_set);

  for (; !iter_compare (ITER (&beg), ITER (&end));
       iter_next (ITER (&beg))) {
    struct hash_pair *pair = (struct hash_pair *)iter_deref (ITER (&beg));
    union sensor *sensor = (union sensor *)pair->data;
    int fd = sensor_get_selectable (sensor);

    FD_SET (fd, &rd_set);

    if (nfds < fd)
      nfds = fd;
  }

  sel = select (nfds + 1, &rd_set, NULL, NULL, NULL);

  if (sel < 0) {
    log (LOG_ERR, "error in select");
    return -1;
  }
  else {
    if (hash_begin (&mgr->sensors, &beg) == -1
        || hash_end (&mgr->sensors, &end) == -1) {
      log (LOG_ERR, "unable to create hash iterators");
      return -1;
    }

    for (; !iter_compare (ITER (&beg), ITER (&end));
         iter_next (ITER (&beg))) {
      struct hash_pair *pair = (struct hash_pair *)iter_deref (ITER (&beg));
      union sensor *sensor = (union sensor *)pair->data;
      int fd = sensor_get_selectable (sensor);

      if (FD_ISSET (fd, &rd_set)) {
        int r = sensor_read (sensor, reading);

        if (r == 1)
          return *(int *)pair->key;
        else if (r == -1) {
          log (LOG_ERR, "error reading sensor");
          return -1;
        }
      }
    }
  }

  log (LOG_ERR, "unknown error");

  return -1;
}
